package meatslope2.gameengine.input;

import java.util.Queue;
import java.util.concurrent.LinkedBlockingQueue;

import javax.microedition.khronos.opengles.GL10;

import meatslope2.application.Game;
import meatslope2.gameengine.gameobjects.IPlayerAction;
import meatslope2.gameengine.primatives.Vector2;
import android.view.KeyEvent;
import android.view.MotionEvent;

/**
 * TODO: document weird motion event quirks
 * TODO: documentation
 * @author Mitchell Thelen
 *
 */
public final class InputSystem 
{
	// ===========================================================
	// Constants
	// ===========================================================
	
	private static final int MAX_NUM_TOUCHES = 5;
	
	// ===========================================================
	// Members
	// ===========================================================
	
	private final Queue<MotionEvent> eventQueue = new LinkedBlockingQueue<MotionEvent>(); //Must be thread safe
	private final IPlayerAction player;
	private final HUD hud;
	private Game game;
	
	//Keep track of the touches on screen
	private final Vector2[] touchPoints;
	private int currentNumTouches = 0;
	
	// ===========================================================
	// Getters/Setters
	// ===========================================================
	
	public final HUD getHUD()
	{
		return this.hud;
	}
	
	public void setGame(Game game) 
	{
		this.game = game;
	}
	
	// ===========================================================
	// Constructors
	// ===========================================================
	
	public InputSystem(IPlayerAction player)
	{
		this.player = player;
		this.hud = new HUD();
		
		touchPoints = new Vector2[MAX_NUM_TOUCHES];
		for(int i = 0; i < touchPoints.length; i++)
			touchPoints[i] = new Vector2();
	}
	
	// ===========================================================
	// Methods
	// ===========================================================
	
    public final boolean onKeyDown(int keyCode, KeyEvent event) 
    {
    	if (keyCode == KeyEvent.KEYCODE_BACK) 
    	{
			//Would you like to quit the game?
			return true;
    	}
    	else if (keyCode == KeyEvent.KEYCODE_MENU)
    	{
    		//Show game menu
    		return true;
    	}
    	
        return false;
    }

	public final boolean onTouchEvent(MotionEvent event) 
	{
		eventQueue.add(event);
		return true;
	}
	
	public final void consumeEvents()
	{
		while(!eventQueue.isEmpty())
		{
			final MotionEvent motionEvent = eventQueue.remove();
			this.currentNumTouches = motionEvent.getPointerCount();
			
			if(finalEvent(motionEvent))
			{
				this.player.stop();
				this.hud.resetButtons();
				
				continue;
			}
			
			for(int i = 0; i < this.currentNumTouches; i++)
			{
				final float x = motionEvent.getX(i);
				final float y = game.getHeight() - motionEvent.getY(i);
				
				//add to touch points
				touchPoints[i].set(x, y);
			}
			
			hud.performButtonPresses();
		}
	}
	
	/**
	 * Says whether this is the final event before the player takes his fingers off the pad
	 * @return
	 */
	private boolean finalEvent(MotionEvent motionEvent)
	{
		final int action = motionEvent.getActionMasked();
		
		if(action == MotionEvent.ACTION_CANCEL)
			return true;
		if((action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_POINTER_UP) && this.currentNumTouches <= 1)
			return true;
		
		return false;
	}

	public class HUD
	{
		// ===========================================================
		// Constants
		// ===========================================================
		
		private final Vector2 LEFT_ARROW_POSITION = new Vector2(20, 20);
		private final Vector2 RIGHT_ARROW_POSITION = new Vector2(160, 20);
		
		// ===========================================================
		// Members
		// ===========================================================
		
		private final IMovementButton[] leftButtons = new IMovementButton[]
		{
			new MovementButton(MovementButton.LEFT_ARROW, (int)LEFT_ARROW_POSITION.x, (int)LEFT_ARROW_POSITION.y)
			{
				public void performAction() { player.runLeft(); }
				public void performJump() { player.jump(); }
			},
			
			new MovementButton(MovementButton.RIGHT_ARROW, (int)RIGHT_ARROW_POSITION.x, (int)RIGHT_ARROW_POSITION.y)
			{
				public void performAction() { player.runRight(); }
				public void performJump() { player.jump(); }
			}
		};

		// ===========================================================
		// Methods (Button Iterations)
		// ===========================================================
		
		public final void performButtonPresses() 
		{
			beginButtonTouches();
			
			//apply button performers to player
			for(int i = 0; i < currentNumTouches; i++)
			{
				final Vector2 touchPoint = touchPoints[i];
				
				if(touchPoint.x <= game.getWidth() / 2)
					checkLeftButtons(touchPoint);
/*				else
					checkRightButtons(touchPoint);*/
			}
			
			endButtonTouches();
		}

		private void beginButtonTouches() 
		{
			for(int i = 0; i < leftButtons.length; i++)
			{
				IMovementButton button = leftButtons[i];
				button.startTouches();
			}
		}
		
		private void endButtonTouches() 
		{
			for(int i = 0; i < leftButtons.length; i++)
			{
				IMovementButton button = leftButtons[i];
				button.endTouches();
			}
		}

		private final void resetButtons() 
		{
			for(int i = 0; i < leftButtons.length; i++)
			{
				IMovementButton button = leftButtons[i];
				button.reset();
			}
		}

		private final void checkLeftButtons(Vector2 touchPosition) 
		{
			for(int i = 0; i < leftButtons.length; i++)
			{
				IButton button = leftButtons[i];
				if(button.pressed(touchPosition))
					button.performAction();
			}
		}
		
		// ===========================================================
		// Methods
		// ===========================================================
		
		public final void draw(GL10 pGL)
		{
			for(int i = 0; i < leftButtons.length; i++)
			{
				IButton button = leftButtons[i];
				button.draw(pGL);
			}
		}
	}
}
